/*
This file is part of Jumpers.

Jumpers is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Jumpers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Jumpers.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.chaoticengine.cgll.input;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.chaoticengine.cgll.entity.Entity;
import org.chaoticengine.cgll.entity.EntityManager;
import org.chaoticengine.cgll.entity.component.Command;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.KeyListener;
import org.newdawn.slick.MouseListener;

/**
 * The InputManager is used to handle all input events, by calling the
 * corresponding command as configured in the config.properties file.
 *
 * @author Matt v.d. Westhuizen
 */
public class InputManager implements MouseListener, KeyListener {
    private Map<Integer, String> keys = null;
    private Map<Integer, String> shiftKeys = null;
    private List<Integer> activeKeys = null;
    private Input input = null;
    /** True if any shift key is down, else false. */
    private boolean shift = false;

    public InputManager(GameContainer gc) throws FileNotFoundException, IOException {
        keys = new HashMap<Integer, String>();
        shiftKeys = new HashMap<Integer, String>();
        activeKeys = new ArrayList<Integer>();

        // Load the properties file if it exists
        Properties p = new Properties();
        InputStream fis = new FileInputStream("config.properties");
        p.load(fis);
        fis.close();

        // Load key mappings
        int i = 0;
        String key = p.getProperty("key" + i);
        String cmd = p.getProperty("command" + i);
        boolean shifted = false;

        while ((key != null) && (cmd != null)) {
            Field f = null;
            int keyCode = -1;

            if (key.startsWith("+")) {
                key = key.substring(1);
                shifted = true;
            }

            try {
                f = Input.class.getDeclaredField(key);
            } catch (NoSuchFieldException nsfEx) {
                System.out.println("Error: Key not found '" + key + "'.");
            }

            if (f != null) {
                try {
                    keyCode = f.getInt(gc.getInput());
                } catch (IllegalAccessException iaEx) {
                    System.out.println("Error: Key not accessible '" + key + "'.");
                }
            }
            
            if (keyCode >= 0) {
                if (shifted) {
                    shiftKeys.put(keyCode, cmd);
                } else {
                    keys.put(keyCode, cmd);
                }
            }

            // Get next items
            key = p.getProperty("key" + i);
            cmd = p.getProperty("command" + i);
            shifted = false;
            i++;
        }
    }
    
    private Command getCommand(String name) {
        if (name == null) {
            return(null);
        }
        
        Entity player = EntityManager.getInstance().getPlayer();

        if (player == null) {
            return(null);
        }

        Map<String, Command> commands = player.getComponentManager().getCommands();
        Command cmd = commands.get(name);

        return(cmd);
    }

    // Mouse support methods
    public void mouseClicked(int button, int x, int y, int clickCount) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mouseMoved(int oldX, int oldY, int newX, int newY) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mousePressed(int button, int x, int y) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mouseReleased(int button, int x, int y) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mouseWheelMoved(int change) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void inputEnded() {
        return;
    }

    public boolean isAcceptingInput() {
        return(true);
    }

    public void setInput(Input input) {
        this.input = input;
    }

    // Keyboard support methods
    public void keyPressed(int key, char c) {
        activeKeys.add(key);

        if ((key == Input.KEY_LSHIFT) || (key == Input.KEY_RSHIFT)) {
            if (!shift) { // Shift wasn't pressed before
                shift = true;

                // Modify any existing shift-able key-presses
                Iterator<Integer> ksIt = activeKeys.iterator();
                while (ksIt.hasNext()) {
                    Integer k = ksIt.next();

                    if (shiftKeys.containsKey(k)) {
                        Command oldCmd = getCommand(keys.get(k));
                        Command newCmd = getCommand(shiftKeys.get(k));

                        if (oldCmd != null) {
                            oldCmd.disable();

                            if (newCmd != null) {
                                newCmd.enable();
                            }
                        }
                    }
                }

                return;
            }
        }

        Command cmd = null;
        if (shift) {
            cmd = getCommand(shiftKeys.get(key));

            if (cmd == null) { // no such shift key
                cmd = getCommand(keys.get(key));
            }
        } else {
            cmd = getCommand(keys.get(key));
        }

        if (cmd != null) {
            cmd.enable();
        }
    }

    public void keyReleased(int key, char c) {
        activeKeys.remove(new Integer(key));
        
        if ((key == Input.KEY_LSHIFT) || (key == Input.KEY_RSHIFT)) {
            if (shift) { // Shift was pressed before
                shift = false;
                
                // Modify any existing shifted keys back to normal ones
                Iterator<Integer> ksIt = activeKeys.iterator();
                while (ksIt.hasNext()) {
                    Integer k = ksIt.next();

                    if (keys.containsKey(k)) {
                        Command oldCmd = getCommand(shiftKeys.get(k));
                        Command newCmd = getCommand(keys.get(k));

                        if (oldCmd != null) {
                            oldCmd.disable();

                            if (newCmd != null) {
                                newCmd.enable();
                            }
                        }
                    }
                }

                return;
            }
        }

            Command cmd = null;
            if (shift) {
                cmd = getCommand(shiftKeys.get(key));

                if (cmd == null) { // no such shift key
                    cmd = getCommand(keys.get(key));
                }
            } else {
                cmd = getCommand(keys.get(key));
            }

            if (cmd != null) {
                cmd.disable();
            }
    }

    public void mouseDragged(int i, int i1, int i2, int i3) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void inputStarted() {
        //throw new UnsupportedOperationException("Not supported yet.");
    }


}
